package com.jms.mybatis.plus.context;

import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;
import com.jms.framework.jdbc.DataSourceManagement;
import com.jms.framework.jdbc.transaction.adaper.TransactionManagementAdapter;
import com.jms.mybatis.plus.factory.MapperProxyFactory;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import javax.sql.DataSource;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;

/**
 * MyBatis上下文实现类，负责管理MyBatis的全局配置和SqlSessionFactory的创建
 * 通过单例模式提供全局访问点
 * @author jms
 * @since 2025-01-05
 * @version 1.0.0
 */
public class MyBatisContextImpl implements MybatisContext {

    private List<String> mapperScanClassNames = new ArrayList<>();

    // 单例模式实现，确保全局只有一个MyBatisContextImpl实例
    private static volatile MyBatisContextImpl instance;

    // SqlSessionFactory是创建SqlSession的工厂，全局共享
    private SqlSessionFactory sqlSessionFactory;
    // 日志记录器
    private static final Logger logger = LogManager.getLogger(MyBatisContextImpl.class);

    /**
     * 获取SqlSession实例
     * @return SqlSession实例，用于执行SQL操作
     */
    public SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }

    @Override
    public SqlSession getSqlSession(Connection connection) {
        return sqlSessionFactory.openSession(connection);
    }

    /**
     * 单例模式的实例获取方法
     * @return MyBatisContextImpl的实例
     */
    public static MyBatisContextImpl getInstance() {
        if (instance == null) {
            synchronized (MyBatisContextImpl.class) {
                if (instance == null) {
                    instance = new MyBatisContextImpl();
                }
            }
        }
        return instance;
    }

    /**
     * 获取数据源配置
     * @return DataSource实例，根据配置信息创建
     */
    @Override
    public DataSource getDataSource() {
        return DataSourceManagement.getInstance().getDataSource();
    }

    // 私有构造方法，防止外部实例化
    private MyBatisContextImpl() {
    }

    /**
     * 获取Mapper接口的代理实现
     * @param mapperInterface Mapper接口类
     * @param <T> Mapper接口类型
     * @return Mapper接口的实现类实例
     */
    public <T> T getMapper(Class<T> mapperInterface) {
        Connection connection = TransactionManagementAdapter.getInstance().getCurrentConnection();
        SqlSession sqlSession = null;
        if (connection != null && sqlSessionFactory != null) {
            sqlSession = sqlSessionFactory.openSession(connection);
        }
        return MapperProxyFactory.getProxy(mapperInterface, sqlSessionFactory, sqlSession);
    }

    public void setMapperScanClassNames(List<String> mapperScanClassNames) {
        this.mapperScanClassNames = mapperScanClassNames;
    }

    /**
     * 初始化MyBatis配置和SqlSessionFactory
     */
    @SuppressWarnings("unchecked")
    @Override
    public void init() {
        // 设置GenericType解析器，用于解析Mapper接口的泛型类型
        GenericTypeUtils.setGenericTypeResolver((clazz, genericIfc) -> {
            Class<?>[] result = new Class<?>[1];
            Type[] type = clazz.getGenericInterfaces();
            for (Type t : type) {
                if (t instanceof ParameterizedTypeImpl) {
                    ParameterizedTypeImpl pti = (ParameterizedTypeImpl) t;
                    if (pti.getRawType().equals(BaseMapper.class)) {
                        result[0] = (Class<?>) pti.getActualTypeArguments()[0];
                    }
                }
            }
            return result;
        });
        // 创建MybatisConfiguration对象，并设置环境、日志实现类等配置
        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration(new Environment("Production", new JdbcTransactionFactory(), getDataSource()));
        mybatisConfiguration.setLogImpl(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);

        if (mapperScanClassNames != null && !mapperScanClassNames.isEmpty()) {
            // 遍历每个全限定类名，尝试加载类并添加到MyBatis配置中
            for (String fullyLimitedClassName : mapperScanClassNames) {
                try {
                    Class<? extends Mapper<?>> cl = (Class<? extends Mapper<?>>) Class.forName(fullyLimitedClassName);
                    if (cl.isAnnotationPresent(org.apache.ibatis.annotations.Mapper.class)) {
                        mybatisConfiguration.addMapper(cl);
                    }
                } catch (ClassNotFoundException e) {
                    logger.error(String.format("Not find the class: %s.", fullyLimitedClassName), e);
                }
            }
        }
        // 使用MybatisConfiguration构建SqlSessionFactory
        sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(mybatisConfiguration);
    }

    /**
     * 销毁方法，释放SqlSessionFactory资源
     */
    @Override
    public void destroy() {
        sqlSessionFactory = null;
    }
}
